1 package uba.db;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashMap;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.Map;
9
10 import uba.db.column.CharColumnSpecification;
11 import uba.db.column.Column;
12 import uba.db.column.IntegerColumnSpecification;
13 import uba.db.impl.memory.MemoryTable;
14 import uba.db.table.InsertException;
15 import uba.db.table.Row;
16 import uba.db.table.Table;
17 import uba.db.table.TableSchema;
18 import uba.db.table.io.TableReader;
19
20 /***
21 * Clase base utilizada para simplificar la creación de clases que implementen
22 * {@link uba.db.Database}.
23 *
24 * @version $Revision: 1.5 $
25 */
26 public abstract class DatabaseBehavior implements Database {
27 private Map systemTables;
28 private Map userTables;
29 private Map dataTypeIdMap;
30 private Table tablesTable;
31 private Table columnsTable;
32 private Table dataTypesTable;
33
34 /***
35 * Este método es equivalente a: DatabaseBehavior(null)
36 *
37 * @see #DatabaseBehavior(Object)
38 */
39 public DatabaseBehavior() throws DatabaseInitializationException {
40 this(null);
41 }
42
43 /***
44 * Las subclases deben invocar este constructor si desean inicializar la
45 * instancia de la base de datos usando una configuración particular.
46 *
47 * @param configuration
48 * "configuración" a utilizar, cada sub-clase debe hacer un cast
49 * de este objeto según sea necesario.
50 *
51 * @throws DatabaseInitializationException
52 * si no se pudo inicializar la base de datos.
53 */
54 public DatabaseBehavior(Object configuration) throws DatabaseInitializationException {
55 configureDatabaseUsing(configuration);
56 initializeSystemTables();
57 initializeSystemTablesMap();
58 initializeDataTypeIdMap();
59 initializeUserTablesMap();
60 initializeUserTables();
61 }
62
63 private void initializeUserTablesMap() {
64 userTables = new HashMap();
65 }
66
67 /***
68 * Las sub-clases pueden sobre escribir este método si desean inicializar las
69 * @throws DatabaseInitializationException
70 */
71 protected void initializeUserTables() throws DatabaseInitializationException {}
72
73 /***
74 * Las sub-clases pueden sobre-escribir este método para configurar la
75 * instancia antes de inicializar la base de datos.
76 *
77 * @param configuration
78 * "configuración" a utilizar, cada sub-clase debe hacer un cast
79 * de este objeto según sea necesario.
80 *
81 *
82 * @throws DatabaseInitializationException
83 * si no se pudo inicializar la base de datos.
84 *
85 * @see #createTablesTable
86 * @see #createColumnsTable
87 * @see #createDataTypesTable
88 */
89 protected void configureDatabaseUsing(Object configuration)
90 throws DatabaseInitializationException {}
91
92 private void initializeSystemTables() throws DatabaseInitializationException {
93 try {
94 tablesTable = createTablesTable();
95 columnsTable = createColumnsTable();
96 dataTypesTable = createDataTypesTable();
97 } catch (Exception e) {
98 throw new DatabaseInitializationException(e);
99 }
100 }
101
102 /***
103 * @see uba.db.Database#tableNamed(java.lang.String)
104 */
105 public Table tableNamed(String tableName) throws UnknowTableName {
106 Table table = basicTableNamed(tableName.toLowerCase());
107
108 if (table == null) {
109 throw new UnknowTableName(this, tableName);
110 }
111
112 return table;
113 }
114
115 /***
116 * @see uba.db.Database#containsTableNamed(java.lang.String)
117 */
118 public boolean containsTableNamed(String tableName) {
119 return basicTableNamed(tableName) != null;
120 }
121
122 /***
123 * Las sub-clases deben implementar este método creando la instancia
124 * concreta de la tabla a utilizar.
125 *
126 * @param tableSchema
127 * esquema de la tabla a crear.
128 *
129 * @return una instancia concreta de {@link Table} que depende de la
130 * implementación.
131 *
132 * @throws TableCreationException
133 * si no se pudo crear la tabla.
134 */
135 protected abstract Table basicCreateTable(TableSchema tableSchema)
136 throws TableCreationException;
137
138 /***
139 * @see uba.db.Database#createTable(uba.db.table.TableSchema)
140 */
141 public Table createTable(TableSchema tableSchema) throws TableAlreadyExistsException,
142 TableCreationException {
143 if (containsTableNamed(tableSchema.tableName())) {
144 throw new TableAlreadyExistsException(this, tableSchema);
145 }
146
147 Table newTable = basicCreateTable(tableSchema);
148 registerInSystemTables((Integer) idGenerator().nextId(), newTable);
149
150 return newTable;
151 }
152
153 /***
154 * Las sub-clases deben implementar este método retornando un objeto que se
155 * encarga de crear los ids para las nuevas tablas.
156 */
157 protected abstract IdGenerator idGenerator();
158
159 private void registerInSystemTables(Integer tableId, Table table) {
160 addToUserTablesMap(table);
161 addToTablesTable(tableId, table);
162 addToColumnsTable(tableId, table);
163 }
164
165 /***
166 * @see uba.db.Database#tables()
167 */
168 public Collection tables() {
169 List tables = new ArrayList(systemTables());
170 tables.addAll(userTables());
171 return tables;
172 }
173
174 private void initializeDataTypeIdMap() {
175 dataTypeIdMap = new HashMap();
176 TableReader reader = dataTypesTable.reader();
177 while (reader.hasMoreRows()) {
178 Row row = reader.fetchRow();
179 dataTypeIdMap.put(row.valueAt(1), row.valueAt(0));
180 }
181 reader.close();
182 }
183
184 private void initializeSystemTablesMap() {
185 systemTables = new HashMap();
186 systemTables.put(tablesTable.name(), tablesTable);
187 systemTables.put(columnsTable.name(), columnsTable);
188 systemTables.put(dataTypesTable.name(), dataTypesTable);
189 }
190
191 /***
192 * Crea la instancia concreta de la tabla utilizada para guardar las columnas de
193 * cada tabla del usuario. Por default utilizar un {@link MemoryTable} como
194 * implementación.
195 *
196 * @see SystemTableSchemas#COLUMNS_SCHEMA
197 */
198 protected Table createColumnsTable() {
199 return new MemoryTable(SystemTableSchemas.COLUMNS_SCHEMA);
200 }
201
202 /***
203 * Crea la instancia concreta de la tabla utilizada para guardar las tablas del
204 * usuario. Por default utilizar un {@link MemoryTable} como implementación.
205 *
206 * @see SystemTableSchemas#COLUMNS_SCHEMA
207 */
208 protected Table createTablesTable() throws Exception {
209 return new MemoryTable(SystemTableSchemas.TABLES_SCHEMA);
210 }
211
212 private Table createDataTypesTable() {
213 Table dataTypes = new MemoryTable(SystemTableSchemas.DATATYPES_SCHEMA);
214 insertIntoSystemTable(dataTypes,
215 new Integer(1),
216 IntegerColumnSpecification.DATATYPE_DISPLAY_STRING);
217 insertIntoSystemTable(dataTypes,
218 new Integer(2),
219 CharColumnSpecification.DATATYPE_DISPLAY_STRING);
220
221 return dataTypes;
222 }
223
224 private void insertIntoSystemTable(Table table, Object col1, Object col2) {
225 insertIntoSystemTable(table, new Object[] { col1, col2 });
226 }
227
228 private void insertIntoSystemTable(Table table, Object[] objects) {
229 try {
230 table.insert(objects);
231 } catch (InsertException e) {
232 throw new Error(e);
233 }
234 }
235
236 /***
237 * Agrega una tabla al diccionario de tablas de usuario.
238 *
239 * @param table la tabla a agregar.
240 */
241 protected void addToUserTablesMap(Table table) {
242 userTables.put(table.name(), table);
243 }
244
245 /***
246 * Agrega todas las columnas de una tabla, a la tabla {@link #columnsTable()} del
247 * catalogo.
248 *
249 * @param tableId identificador de la tabla.
250 * @param table la tabla.
251 */
252 protected void addToColumnsTable(Integer tableId, Table table) {
253 Iterator iter = table.columns().iterator();
254 int columnId = 0;
255 while (iter.hasNext()) {
256 columnId++;
257 Column column = (Column) iter.next();
258 insertIntoSystemTable(columnsTable, tableId, new Integer(columnId), column
259 .name(), dataTypeIdMap.get(column.dataTypeDisplayString()));
260 }
261 }
262
263 private void insertIntoSystemTable(Table table, Object col1, Object col2,
264 Object col3, Object col4) {
265 insertIntoSystemTable(table, new Object[] { col1, col2, col3, col4 });
266 }
267
268 /***
269 * Agrega el id y nombre de la tabla a la tabla {@link #tablesTable()} del
270 * catalogo.
271 *
272 * @param tableId identificador de la tabla.
273 * @param table la tabla.
274 */
275 protected void addToTablesTable(Integer tableId, Table table) {
276 insertIntoSystemTable(tablesTable, tableId, table.name());
277 }
278
279 /***
280 * @see uba.db.Database#systemTables()
281 */
282 public Collection systemTables() {
283 return systemTables.values();
284 }
285
286 /***
287 * @see uba.db.Database#userTables()
288 */
289 public Collection userTables() {
290 return userTables.values();
291 }
292
293 /***
294 * @see uba.db.DatabaseBehavior#basicTableNamed(java.lang.String)
295 */
296 protected Table basicTableNamed(String tableName) {
297 Table table = (Table) systemTables.get(tableName);
298
299 if (table == null) {
300 table = (Table) userTables.get(tableName);
301 }
302
303 return table;
304 }
305
306 /***
307 * @see uba.db.Database#tablesTable()
308 */
309 public Table tablesTable() {
310 return tablesTable;
311 }
312
313 /***
314 * @see uba.db.Database#dataTypesTable()
315 */
316 public Table dataTypesTable() {
317 return dataTypesTable;
318 }
319
320 /***
321 * @see uba.db.Database#columnsTable()
322 */
323 public Table columnsTable() {
324 return columnsTable;
325 }
326 }